package com.jdone.compus.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.jdone.compus.dto.PaymentProperties;
import com.jdone.compus.model.BlowOrderReconError;
import com.jdone.compus.model.BlowUsageRecord;
import com.jdone.compus.repository.BlowOrderReconErrorRepository;
import com.jdone.compus.repository.BlowUsageRecordRepository;
import com.jdone.compus.enums.OrderStatus;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 单条订单的独立事务处理服务（REQUIRES_NEW）
 */
@Service
public class OrderRecordService {
   /* private final Logger logger = LoggerFactory.getLogger(OrderRecordService.class);
    private final BlowUsageRecordRepository recordRepository;
    private final PaymentProperties paymentProperties;
    private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public OrderRecordService(BlowUsageRecordRepository recordRepository,
                              PaymentProperties paymentProperties) {
        this.recordRepository = recordRepository;
        this.paymentProperties = paymentProperties;
    }

    *//**
     * 独立事务处理一条远端订单：查找 -> 更新或新增
     * Propagation.REQUIRES_NEW 保证本方法在新事务中执行，失败回滚不会影响调用者事务。
     *//*
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processSingleOrder(JsonNode orderNode) {
        // 读取远端字段
        String remoteOutOrderNo = textOf(orderNode, "outOrderNo");
        Integer remoteStatus = nodeInt(orderNode, "status");
        Long orderAmountCents = nodeLong(orderNode, "orderAmount");

        Optional<BlowUsageRecord> opt = Optional.empty();

        // 再尝试用 outOrderNo 去匹配 businessOrderNo
        if (remoteOutOrderNo != null && !remoteOutOrderNo.isEmpty()) {
            opt = recordRepository.findByBusinessOrderNo(remoteOutOrderNo);
        }

        if (opt.isPresent()) {
            BlowUsageRecord local = opt.get();
            boolean needSave = false;


            //新增内容 根据status状态来和查询的opt的    @ApiModelProperty(value = "'CREATED', 'PAID', 'USED','CANCELLED' 订单的状态")
            //    private OrderStatus orderStatus;来对应，

            // remote
            if (remoteStatus != null && remoteStatus == 1 && local.getOrderStatus() != OrderStatus.PAID) {
                local.setOrderStatus(OrderStatus.PAID);
                needSave = true;
            }

            // 回写金额：分 -> 元
            if ((local.getFee() == null || local.getFee().compareTo(BigDecimal.ZERO) == 0)
                    && orderAmountCents != null) {
                local.setFee(BigDecimal.valueOf(orderAmountCents).divide(BigDecimal.valueOf(100)));
                needSave = true;
            }

            // 回写 payTime（写到 returnTime）
            String payTimeStr = textOf(orderNode, "payTime");
            if (payTimeStr != null && !payTimeStr.isEmpty()) {
                try {
                    LocalDateTime payTime = LocalDateTime.parse(payTimeStr, dtf);
                    local.setReturnTime(payTime);
                    needSave = true;
                } catch (Exception ignored) {}
            }


            if (needSave) {
                recordRepository.save(local); // 在 REQUIRES_NEW 事务下保存
            }
        } else {
            BlowUsageRecord newRec = new BlowUsageRecord();
            if (orderAmountCents != null) {
                newRec.setFee(BigDecimal.valueOf(orderAmountCents).divide(BigDecimal.valueOf(100)));
            } else {
                newRec.setFee(BigDecimal.ZERO);
            }

            // 时间
            String createTime = textOf(orderNode, "createTime");
            String payTime = textOf(orderNode, "payTime");
            LocalDateTime t = null;
            try {
                if (createTime != null && !createTime.isEmpty()) t = LocalDateTime.parse(createTime, dtf);
                else if (payTime != null && !payTime.isEmpty()) t = LocalDateTime.parse(payTime, dtf);
            } catch (Exception ex) {
                logger.warn("解析远端时间失败: {}", ex.getMessage());
            }
            if (t == null) t = LocalDateTime.now();
            newRec.setBorrowTime(t);

            // openId/subject -> userAccount/username
            String openId = textOf(orderNode, "openId");
            if (openId == null) openId = textOf(orderNode, "openid");
            newRec.setUserAccount(openId == null ? "" : openId);
            String subject = textOf(orderNode, "subject");
            newRec.setUsername(subject == null ? "" : subject);

            newRec.setMinutes(0);
            if (remoteStatus != null && remoteStatus == 1) newRec.setOrderStatus(OrderStatus.USED);
            else newRec.setOrderStatus(OrderStatus.CREATED);
            // 保存（若违反唯一索引会抛异常并回滚此事务）
            recordRepository.save(newRec);
        }
    }

*/


    private final Logger logger = LoggerFactory.getLogger(OrderRecordService.class);
    private final BlowUsageRecordRepository recordRepository;
    private final PaymentProperties paymentProperties;
    private final BlowOrderReconErrorRepository reconErrorRepository;
    private final RestTemplate restTemplate;
    private final ObjectMapper objectMapper;
    private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    public OrderRecordService(BlowUsageRecordRepository recordRepository,
                              PaymentProperties paymentProperties,
                              BlowOrderReconErrorRepository reconErrorRepository) {
        this.recordRepository = recordRepository;
        this.paymentProperties = paymentProperties;
        this.reconErrorRepository = reconErrorRepository;
        this.restTemplate = new RestTemplate();
        this.objectMapper = new ObjectMapper();
    }

    /**
     * 修改：新增 doUpload 参数，若为 false 则只本地记录异常，不进行远端上报
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processSingleOrder(JsonNode orderNode, boolean doUpload) {
        String remoteOutOrderNo = textOf(orderNode, "outOrderNo");
        Integer remoteStatus = nodeInt(orderNode, "status");
        Long orderAmountCents = nodeLong(orderNode, "orderAmount");
        String transactionNo = textOf(orderNode, "transactionNo"); // 支付渠道流水（可能字段名不同）
        String payChannel = textOf(orderNode, "payChannel");
        String payTimeStr = textOf(orderNode, "payTime");

        Optional<BlowUsageRecord> opt = Optional.empty();
        if (remoteOutOrderNo != null && !remoteOutOrderNo.isEmpty()) {
            opt = recordRepository.findByBusinessOrderNo(remoteOutOrderNo);
        }

        // 用于判断是否发现异常需要上报/记录
        List<BlowOrderReconError> foundErrors = new ArrayList<>();

        if (opt.isPresent()) {
            BlowUsageRecord local = opt.get();
            boolean needSave = false;

            // 1) 状态映射并比较：远端 -> 本地
            OrderStatus expected = mapRemoteStatusToLocal(remoteStatus);
            if (expected != null && local.getOrderStatus() != expected) {
                // 如果要变为 CANCELLED，生成退款单号并保存
                if (expected == OrderStatus.CANCELLED) {
                    String outRefundNo = generateOutRefundNo();
                    local.setOutRefundNo(outRefundNo); // 需要 BlowUsageRecord 有该字段并映射到 out_refund_no
                    logger.info("订单 {} 状态需修改为 CANCELLED，生成退款单号 {}", local.getBusinessOrderNo(), outRefundNo);
                }
                local.setOrderStatus(expected);
                needSave = true;

                // 记录状态不符异常
                BlowOrderReconError err = buildReconError(local, transactionNo, payChannel, orderAmountCents, local.getFee(), 6,
                                        "状态不符，本地：" + local.getOrderStatus() + ", 远端：" + remoteStatus);
                foundErrors.add(err);
            }

            // 2) 金额核对（本地 fee 单位：元 -> 转为分进行比较）
            Long localAmountCents = local.getFee() != null ? local.getFee().multiply(BigDecimal.valueOf(100)).longValue() : 0L;
            if (orderAmountCents != null && !orderAmountCents.equals(localAmountCents)) {
                // 覆盖为远端金额（写入元）
                local.setFee(BigDecimal.valueOf(orderAmountCents).divide(BigDecimal.valueOf(100)));
                needSave = true;

                BlowOrderReconError err = buildReconError(local, transactionNo, payChannel, localAmountCents, BigDecimal.valueOf(orderAmountCents), 3,
                        "金额不一致，本地分:" + localAmountCents + ", 远端分:" + orderAmountCents);
                foundErrors.add(err);
            }

            // 3) 回写 payTime（写到 returnTime） - 保持原逻辑
            if (payTimeStr != null && !payTimeStr.isEmpty()) {
                try {
                    LocalDateTime payTime = LocalDateTime.parse(payTimeStr, dtf);
                    local.setReturnTime(payTime);
                    needSave = true;
                } catch (Exception ignored) {}
            }

            if (needSave) {
                recordRepository.save(local);
            }
        } else {
            // 本地没有找到：需要新建，并把 outOrderNo 写入 businessOrderNo
            BlowUsageRecord newRec = new BlowUsageRecord();
            newRec.setBusinessOrderNo(remoteOutOrderNo); // ***关键：写入远端 outOrderNo 到 businessOrderNo
            if (orderAmountCents != null) {
                newRec.setFee(BigDecimal.valueOf(orderAmountCents).divide(BigDecimal.valueOf(100)));
            } else {
                newRec.setFee(BigDecimal.ZERO);
            }

            // 时间
            String createTime = textOf(orderNode, "createTime");
            LocalDateTime t = null;
            try {
                if (createTime != null && !createTime.isEmpty()) t = LocalDateTime.parse(createTime, dtf);
                else if (payTimeStr != null && !payTimeStr.isEmpty()) t = LocalDateTime.parse(payTimeStr, dtf);
            } catch (Exception ex) {
                logger.warn("解析远端时间失败: {}", ex.getMessage());
            }
            if (t == null) t = LocalDateTime.now();
            newRec.setBorrowTime(t);

            // openId/subject -> userAccount/username
            String openId = textOf(orderNode, "openId");
            if (openId == null) openId = textOf(orderNode, "openid");
            newRec.setUserAccount(openId == null ? "" : openId);
            String subject = textOf(orderNode, "subject");
            newRec.setUsername(subject == null ? "" : subject);

            newRec.setMinutes(0);
            if (remoteStatus != null && remoteStatus == 1) newRec.setOrderStatus(OrderStatus.USED);
            else newRec.setOrderStatus(OrderStatus.CREATED);

            // 保存新记录
            recordRepository.save(newRec);

            // 记录本地缺单异常 (type=1: 业务方缺失订单)
            BlowOrderReconError err = buildReconError(newRec, transactionNo, payChannel, null, BigDecimal.valueOf(orderAmountCents), 1,
                    "本地缺单，已新建记录");
            foundErrors.add(err);
        }

        // 若发现异常则保存异常记录并（可选）上报
        for (BlowOrderReconError e : foundErrors) {
            e.setCreateTime(LocalDateTime.now());
            e.setUpdateTime(LocalDateTime.now());
            BlowOrderReconError saved = reconErrorRepository.save(e);
            if (doUpload) {
                try {
                    boolean ok = uploadSingleReconError(saved, payTimeStr);
                    if (ok) {
                        saved.setUploaded(true);
                    } else {
                        saved.setUploaded(false);
                        saved.setUploadAttempts(saved.getUploadAttempts() + 1);
                    }
                } catch (Exception ex) {
                    logger.warn("上传对账异常时出错: {}", ex.getMessage());
                    saved.setUploaded(false);
                    saved.setUploadAttempts(saved.getUploadAttempts() == null ? 1 : saved.getUploadAttempts() + 1);
                } finally {
                    saved.setUpdateTime(LocalDateTime.now());
                    reconErrorRepository.save(saved);
                }
            }
        }
    }

    // 将远端状态映射为本地 OrderStatus
    private OrderStatus mapRemoteStatusToLocal(Integer remoteStatus) {
        if (remoteStatus == null) return null;
        if (remoteStatus == 1) return OrderStatus.PAID;
        if (remoteStatus == 5) return OrderStatus.CANCELLED;
        return null;
    }

    // 生成退款单号（按你给的生成方式）
    private String generateOutRefundNo() {
        return "TK" + IntStream.range(0, 16)
                .mapToObj(i -> String.valueOf(ThreadLocalRandom.current().nextInt(10)))
                .collect(Collectors.joining());
    }

    // 构造异常记录实体（简化）
    private BlowOrderReconError buildReconError(BlowUsageRecord local, String transactionNo, String payChannel,
                                            Long localAmountCents, BigDecimal remoteAmountCents, int type, String remark) {
        BlowOrderReconError e = new BlowOrderReconError();
        e.setOrderNo(local != null ? local.getBusinessOrderNo() : null);
        e.setTransactionNo(transactionNo);
        e.setPayChannel(payChannel);
        e.setLocalAmount(localAmountCents);
        e.setRemoteAmount(remoteAmountCents);
        e.setErrorType(type);
        e.setLocalStatus(local != null && local.getOrderStatus() != null ? local.getOrderStatus().name() : null);
        e.setRemoteStatus(null); // 可填充 remoteStatus 如需
        e.setRemark(remark);
        return e;
    }

    // 把单条异常上报到远端 /golivepay/bill/upload_error（遵循你的协议）
    private boolean uploadSingleReconError(BlowOrderReconError e, String payTimeStr) {
        String url = "https://pay.goliveplus.cn:666/golivepay/bill/upload_error";
        Map<String, Object> payload = new HashMap<>();
        payload.put("myapp_id", paymentProperties.getMyappId());
        payload.put("bill_status", 2); // 2表示异常
        // bill_date 要求一个日期，例如从 payTimeStr 取日期部分或使用当前日期（具体按你业务）
        String billDate = LocalDateTime.now().toLocalDate().toString();
        if (payTimeStr != null) {
            try {
                LocalDateTime pt = LocalDateTime.parse(payTimeStr, dtf);
                billDate = pt.toLocalDate().toString();
            } catch (Exception ignored) {}
        }
        payload.put("bill_date", billDate);
        payload.put("bill_error_num", 1);

        // 构造 list 数组
        Map<String, Object> item = new HashMap<>();
        item.put("transactionNo", e.getTransactionNo());
        item.put("orderNo", e.getOrderNo());
        item.put("payChannel", e.getPayChannel());
        item.put("orderStatus", e.getLocalStatus() == null ? "" : e.getLocalStatus());
        item.put("billStatus", e.getRemoteStatus() == null ? "" : e.getRemoteStatus());
        item.put("orderAmount", e.getLocalAmount() == null ? "" : String.valueOf(e.getLocalAmount()));
        item.put("billOrderAmount", e.getRemoteAmount() == null ? "" : String.valueOf(e.getRemoteAmount()));
        item.put("payTime", payTimeStr == null ? "" : payTimeStr);
        item.put("type", String.valueOf(e.getErrorType()));

        payload.put("list", Collections.singletonList(item));

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers);
        ResponseEntity<String> resp = restTemplate.postForEntity(url, request, String.class);
        if (resp.getStatusCode() == HttpStatus.OK) {
            // 你可以根据远端返回的 body 来判断是否成功（例如 code==200 等）
            try {
                JsonNode respNode = objectMapper.readTree(resp.getBody());
                int code = respNode.path("code").asInt(0);
                return code == 200;
            } catch (Exception ex) {
                logger.warn("解析上报返回异常: {}", ex.getMessage());
                return false;
            }
        } else {
            logger.warn("上报对账异常请求失败，status: {}", resp.getStatusCodeValue());
            return false;
        }
    }

    // 原 helper 方法
    private String textOf(JsonNode node, String name) {
        if (node.has(name) && !node.get(name).isNull()) return node.get(name).asText();
        String lower = name.toLowerCase();
        if (!lower.equals(name) && node.has(lower) && !node.get(lower).isNull()) return node.get(lower).asText();
        return null;
    }
    private Integer nodeInt(JsonNode node, String name) { /* 同你原有实现 */
        try {
            if (node.has(name) && !node.get(name).isNull()) return node.get(name).asInt();
            String lower = name.toLowerCase();
            if (!lower.equals(name) && node.has(lower) && !node.get(lower).isNull()) return node.get(lower).asInt();
        } catch (Exception ignored) {}
        return null;
    }
    private Long nodeLong(JsonNode node, String name) { /* 同你原有实现 */
        try {
            if (node.has(name) && !node.get(name).isNull()) return node.get(name).asLong();
            String lower = name.toLowerCase();
            if (!lower.equals(name) && node.has(lower) && !node.get(lower).isNull()) return node.get(lower).asLong();
        } catch (Exception ignored) {}
        return null;
    }
}

